import json
import jieba
import jieba.analyse
import jieba.posseg as pseg
import re
filename='article.json'
topK=30 # TF-IDF提取的关键词个数
allowPOS=() #TF-IDF允许的关键词词性

def parse_json_data(filename,topK=30,allowPOS=()):
    '''
    从json中读取爬取的文章,返回关键词集合，关键词到序号的字典，转换成句子二元组的文章列表,关键词序号到权重的字典
    :return:
    '''
    with open(filename,'r',encoding='utf-8') as fp:
        obj=json.load(fp) #obj的数据结构 字典的列表,字典仅有article一个字段  [{'article':文章字符串1},{...},...]
    #转为文章字符串的列表
    article_l=[]
    for a_dict in obj:
        article_l.append(a_dict['article'])
    #使用TF-IDF提取关键词
    str=''.join(article_l)
    weight_pairs=jieba.analyse.extract_tags(str,topK,allowPOS=allowPOS,withWeight=True)
    keyword_set=set([word for word,_ in weight_pairs])
    keyword_dict=dict.fromkeys(keyword_set)
    tmp=list(enumerate(keyword_dict))
    keyword_dict=dict([(tp[1],tp[0]) for tp in tmp]) #关键词作为键,数字编号作为值
    weight_dict=dict([(keyword_dict[word],weight) for word,weight in weight_pairs])#数字编号作为键,权重作为值
    #将文章字符串变为句子的列表
    sentences_l=[]
    for art in article_l:
        sent_set=re.split(r'[\s,;.!。，；！？?]',art)
        sentences_l.append(sent_set)
    #将句子转为（原句，包含的关键词序号的列表）的二元组
    tuple_l=[]
    for art in sentences_l:
        tmp=[]
        for sent in art:
            tuple=form_sentence_keyword_tuple(sent,keyword_dict,keyword_set)
            tmp.append(tuple)
        tuple_l.append(tmp)
    return keyword_set,keyword_dict,tuple_l,weight_dict

def form_sentence_keyword_tuple(sentence,keyword_dict,keyword_set):
    '''
    给定句子字符串,关键词集合,关键词到序号的字典,返回(句子字符串,包含的关键词的序号的列表)的二元组
    '''
    words=pseg.cut(sentence)
    l=[]
    for word in words:
        if(word.word in keyword_set):#是keyword
            l.append(keyword_dict[word.word])  #将其序号加入列表
    return sentence,l


def weight_climb(keyword_set_len, sentence_list, weight_dict, n=5):
    '''
    加权爬山,每次选取权重增加最大的句子
    :param keyword_set_len:
    :param sentence_list: (句子,包含的关键词序号)二元组的列表[(sentence0,[no0,no1...]),...]
    :param weight_dict:
    :param n: 选择句子的数量
    :return: 选出句子的列表
    '''
    s_l=sentence_list[:]
    k_s=set(range(keyword_set_len))#关键词序号的集合
    # print(k_s)
    sents=[] #选中的句子集合
    for i in range(n):
        max=0
        chosen_sent=0 #暂时记录当前最优
        for i in range(len(s_l)):
            sent=s_l[i]
            gain=weight_gain(k_s,weight_dict,sent[1])
            if (gain>max):
                max=gain
                chosen_sent=sent
                chosen_ind=i
        if max:
            sents.append(chosen_sent[0])#记录选中的句子
            k_s=k_s-set(chosen_sent[1])#更新关键词集合
            del s_l[chosen_ind] #从候选中删除选中的句子
        else:#剩余句子权值都为0
            print("所给语料除已显示的摘要,其余句子权重增益全为0(不包含新的关键词),故未能摘取目标数量的句子")
            return sents
    return sents




def weight_gain(keyword_set,weight_dict,include_list):
    '''
    计算句子权重增益
    :param keyword_set:
    :param weight_dict:
    :param include_list: 句子包含的关键词序号列表
    :return: 权重增益
    '''
    gain=0
    for word in include_list:
        if (word in keyword_set):
            gain+=weight_dict[word]
    return gain


def generate_weight_summaries(filename,k,summary_amount,sel_art_num=30):
    '''
    生成多条摘要
    input:
        sel_art_num: 选中生成一条摘要的article的数量
        filename：str 输入文件路径名
        k: int 一个summary里面的句子个数
        summary_amount： int 生成摘要个数
    output: 
        生成多条摘要，输出到名为该目录 W_Summary.txt 的文件

    '''
    keyword_set,keyword_dict,tuple_l,weight_dict=parse_json_data(filename)
   
    f = open("W_Summary.txt",'a',encoding='utf-8')
    for j in range(summary_amount):
        #随机挑选sel_art_num个article
        np.random.shuffle(tuple_l)
        sel_article = tuple_l[:sel_art_num]
        #把它们变成一个二元组列表
        tmp=[]
        for i in sel_article:
            tmp+=i
        sents=weight_climb(len(keyword_set),tmp,weight_dict,k)
        sentence = ""
        for c in sents:
            sentence+=c
            sentence+='。'
        f.write('Summary %d\n'%(j))
        f.write(sentence)
        f.write('\n\n\n\n')       
    f.close()

# 普通的爬山
import numpy as np
def generate_single_summary(SW_tuple,k,sel_art_num=30):
    '''
    生成一条摘要
    input:
        sel_art_num: 选中生成一条摘要的article的数量
        SW_tuple=tuple_l ： [('a', [10]), ('b', [0]),...... ]
        k ： int 是一个摘要里有k个句子 
    return:
        one summary
    '''
    C=set()
    summary = ""
    # 随机生成不重复的120个索引， 索引的值在（0，len(SW_tuple)之间
    ram_num_l = np.random.permutation(len(SW_tuple))# 
    sel_num_l = ram_num_l[:120]
    # 利用随机索引列表，挑选要使用的文档。
    SW_tuple = np.array(SW_tuple)
    S_in = SW_tuple[sel_num_l].flatten()
    maxlen = len(C)
    for i in range(k):
        sentence = ""
        for j in range(len(S_in)):
            for s in S_in[j]:
                C_tem_len =  len(C|set(s[1]))
                if C_tem_len>maxlen:
                    maxlen = C_tem_len
                    C_tem = C|set(s[1])
                    sentence = s[0] 
        C = C_tem
        summary+=sentence
        summary+='。'
    return summary

def generate_summaries(filename,k=3,summary_amount=100,sel_art_num=30):
    '''
    生成多条摘要
    input:
        sel_art_num: 选中生成一条摘要的article的数量
        filename：str 输入文件路径名
        k: int 一个summary里面的句子个数
        summary_amount： int 生成摘要个数
    output: 
        生成多条摘要，输出到名为该目录 Summary.txt 的文件

    '''
    f = open("summary.txt",'a',encoding='utf-8')
    keyword_set,keyword_dict,tuple_l,weight_dict=parse_json_data(filename)
    for i in range(summary_amount):
        summary = generate_single_summary(tuple_l,k,sel_art_num)
        f.write('Summary %d\n'%(i))
        f.write(summary)
        f.write('\n\n\n\n')       
    f.close()


import sys
if __name__ =='__main__':
    # 获取命令行参数
    '''
    cmd运行命令 ： python climb_mountain(1).py filename k summary_amount types
    如：python climb_mountain(1).py article.json 3 100 1 
    参数说明：
        filename: json 文件名
        k: int 每个摘要的句子个数
        summary_amount:int 生成摘要个数
        types：int 如果是1 ，则是加权爬山；其它，则是普通爬山
    输出:
        普通爬山输出到 Summary.txt
        加权爬山输出到 W_Summary.txt
    '''
    filename = sys.argv[1]
    k = int(sys.argv[2])
    summary_amount = int(sys.argv[3])
    types = 0
    if len(sys.argv)==5:
        types = int(sys.argv[4])
    if(types==1):# 加权爬山
        generate_weight_summaries(filename,k,summary_amount)
    else:# 普通爬山
        generate_summaries(filename,k,summary_amount)